
use windows::core::Error as WinError;
use windows::Win32::System::Memory;



#[derive(Debug)]
pub struct OsIpcSharedMemory {
    pub(super) handle: WinHandle,
    view_handle: MEMORYMAPPEDVIEW_HANDLE,
    pub(super) length: usize,
}

unsafe impl Send for OsIpcSharedMemory {}
unsafe impl Sync for OsIpcSharedMemory {}

impl Drop for OsIpcSharedMemory {
    fn drop(&mut self) {
        unsafe {
            let result = windows::Win32::System::Memory::UnmapViewOfFile(self.view_handle);
            assert!(result.as_bool() || std::thread::panicking());
        }
    }
}

impl Clone for OsIpcSharedMemory {
    fn clone(&self) -> OsIpcSharedMemory {
        OsIpcSharedMemory::from_handle(super::utils::dup_handle(&self.handle).unwrap(), self.length).unwrap()
    }
}

use std::ops::Deref;

use windows::Win32::System::Memory::MEMORYMAPPEDVIEW_HANDLE;
#[cfg(feature = "windows-shared-memory-equality")]
use windows::Win32::{
    Foundation::BOOL,
    System::LibraryLoader::{GetProcAddress, LoadLibraryA},
};

use super::win_handle::WinHandle;

#[cfg(feature = "windows-shared-memory-equality")]
impl PartialEq for OsIpcSharedMemory {
    fn eq(&self, other: &OsIpcSharedMemory) -> bool {
        self.handle == other.handle
    }
}

impl Deref for OsIpcSharedMemory {
    type Target = [u8];

    #[inline]
    fn deref(&self) -> &[u8] {
        assert!(!self.view_handle.is_invalid() && self.handle.is_valid());
        unsafe { std::slice::from_raw_parts(self.view_handle.0 as _, self.length) }
    }
}

impl OsIpcSharedMemory {
    fn new(length: usize) -> Result<OsIpcSharedMemory, WinError> {
        unsafe {
            assert!(length < u32::max_value() as usize);
            let (lhigh, llow) = (
                length.checked_shr(32).unwrap_or(0) as u32,
                (length & 0xffffffff) as u32,
            );
            let handle = Memory::CreateFileMappingA(
                windows::Win32::Foundation::INVALID_HANDLE_VALUE,
                None,
                Memory::PAGE_READWRITE | Memory::SEC_COMMIT,
                lhigh,
                llow,
                None,
            )?;

            OsIpcSharedMemory::from_handle(WinHandle::new(handle), length)
        }
    }

    // There is no easy way to query the size of the mapping -- you
    // can use NtQuerySection, but that's an undocumented NT kernel
    // API.  Instead we'll just always pass the length along.
    //
    // This function takes ownership of the handle, and will close it
    // when finished.
    pub(super) fn from_handle(handle: WinHandle, length: usize) -> Result<OsIpcSharedMemory, WinError> {
        unsafe {
            let address = Memory::MapViewOfFile(handle.as_raw(), Memory::FILE_MAP_ALL_ACCESS, 0, 0, 0)?;

            Ok(OsIpcSharedMemory {
                handle: handle,
                view_handle: address,
                length: length,
            })
        }
    }

    pub fn from_byte(byte: u8, length: usize) -> OsIpcSharedMemory {
        unsafe {
            // panic if we can't create it
            let mem = OsIpcSharedMemory::new(length).unwrap();
            for element in std::slice::from_raw_parts_mut(mem.view_handle.0 as _, mem.length) {
                *element = byte;
            }
            mem
        }
    }

    pub fn from_bytes(bytes: &[u8]) -> OsIpcSharedMemory {
        unsafe {
            // panic if we can't create it
            let mem = OsIpcSharedMemory::new(bytes.len()).unwrap();
            std::ptr::copy_nonoverlapping(bytes.as_ptr(), mem.view_handle.0 as _, bytes.len());
            mem
        }
    }
}